package com.magi.builder;

import com.magi.config.BuildingConfig;
import com.magi.config.IJavaMapperBuildingConfig;
import com.magi.constant.DefaultMapperID;
import com.magi.core.*;
import org.dom4j.DocumentException;

import java.io.IOException;
import java.util.ArrayList;

public class JavaMapperBuilder implements IJavaMapperBuilder{

    // private final IDatabaseTypeMapping typeMapping;
    private JavaFileBuilder builder;
    private final IJavaMapperBuildingConfig config;

    public JavaMapperBuilder(IJavaMapperBuildingConfig config) {
        this.config = config;
    }

    @Override
    public String Build(TableMetaData metaData) {
        String pojoName = AppUtils.getPojoName(metaData);
        String mapperName = AppUtils.getMapperName(metaData);
        builder = new JavaFileBuilder();
        builder.addPackage(config.getRootMapperPackageName());
        builder.importPackage(config.getRootPojoPackageName() + "." + pojoName);
        builder.importPackage("org.apache.ibatis.session.*");
        builder.importPackage("org.apache.ibatis.io.Resources");
        builder.importPackage("java.util.*");
        builder.importPackage("java.io.IOException");

        builder.addClass(mapperName);


        builder.addField(JavaFileBuilder.Qualifiers.PRIVATE,
                "sqlSessionFactory",
                "SqlSessionFactory",
                null,
                "new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader(\"mybatis-config.xml\"))");
        builder.newLine();

        builder.addConstructor(new FunctionMetaData(){
            {
                setQualifier(JavaFileBuilder.Qualifiers.PUBLIC);
                setExceptions(new String[]{"IOException"});
            }
        });
        builder.newLine();

        this.addSelectMethod(mapperName + "." + DefaultMapperID.SELECT, pojoName);
        builder.newLine();

        this.addUpdateMethod(mapperName + "." + DefaultMapperID.UPDATE, pojoName);
        builder.newLine();

        this.addInsertMethod(mapperName + "." + DefaultMapperID.INSERT, pojoName);
        builder.newLine();

        this.addDeleteMethod(mapperName + "." + DefaultMapperID.DELETE, pojoName);
        builder.newLine();

        return builder.finish();
    }

    private void addSelectMethod(String statement, String pojoName) {
        StringBuilder bodyBuilder = new StringBuilder(Identifiers.TAB);
        bodyBuilder.append(this.getStanderMethodBodyHead());

        bodyBuilder.append(Identifiers.TAB)
                .append("return session.selectList")
                .append(Identifiers.PARENTHESIS_L)
                .append(Identifiers.DOUBLE_QUOT)

                .append(statement)

                .append(Identifiers.DOUBLE_QUOT)
                .append(Identifiers.PARENTHESIS_R)
                .append(Identifiers.SEMICOLON);

        builder.addFunction(
                JavaFileBuilder.Qualifiers.PUBLIC,
                "select",
                new JavaFileBuilder.FunctionArg[]{
                        new JavaFileBuilder.FunctionArg(pojoName, AppUtils.upperCamel2lowerCamel(pojoName))
                },
                bodyBuilder.toString(),
                "List<" + pojoName + ">");
    }

    private void addUpdateMethod(String statement, String pojoName) {
        StringBuilder bodyBuilder = new StringBuilder(Identifiers.TAB);
        bodyBuilder.append(this.getStanderMethodBodyHead());

        bodyBuilder.append(Identifiers.TAB)
                .append("return session.update")
                .append(Identifiers.PARENTHESIS_L)
                .append(Identifiers.DOUBLE_QUOT)

                .append(statement)

                .append(Identifiers.DOUBLE_QUOT)
                .append(Identifiers.PARENTHESIS_R)
                .append(Identifiers.SEMICOLON);

        builder.addFunction(
                JavaFileBuilder.Qualifiers.PUBLIC,
                "update",
                new JavaFileBuilder.FunctionArg[]{
                        new JavaFileBuilder.FunctionArg(pojoName, AppUtils.upperCamel2lowerCamel(pojoName))
                },
                bodyBuilder.toString(),
                Identifiers.TYPE_INT);
    }

    private void addInsertMethod(String statement, String pojoName) {
        StringBuilder bodyBuilder = new StringBuilder(Identifiers.TAB);
        bodyBuilder.append(this.getStanderMethodBodyHead());

        bodyBuilder.append(Identifiers.TAB)
                .append("return session.insert")
                .append(Identifiers.PARENTHESIS_L)
                .append(Identifiers.DOUBLE_QUOT)

                .append(statement)

                .append(Identifiers.DOUBLE_QUOT)
                .append(Identifiers.PARENTHESIS_R)
                .append(Identifiers.SEMICOLON);

        builder.addFunction(
                JavaFileBuilder.Qualifiers.PUBLIC,
                "insert",
                new JavaFileBuilder.FunctionArg[]{
                        new JavaFileBuilder.FunctionArg(pojoName, AppUtils.upperCamel2lowerCamel(pojoName))
                },
                bodyBuilder.toString(),
                Identifiers.TYPE_INT);
    }

    private void addDeleteMethod(String statement, String pojoName) {
        StringBuilder bodyBuilder = new StringBuilder(Identifiers.TAB);
        bodyBuilder.append(this.getStanderMethodBodyHead());

        bodyBuilder.append(Identifiers.TAB)
                .append("return session.delete")
                .append(Identifiers.PARENTHESIS_L)
                .append(Identifiers.DOUBLE_QUOT)

                .append(statement)

                .append(Identifiers.DOUBLE_QUOT)
                .append(Identifiers.PARENTHESIS_R)
                .append(Identifiers.SEMICOLON);

        builder.addFunction(
                JavaFileBuilder.Qualifiers.PUBLIC,
                "delete",
                new JavaFileBuilder.FunctionArg[]{
                        new JavaFileBuilder.FunctionArg(pojoName, AppUtils.upperCamel2lowerCamel(pojoName))
                },
                bodyBuilder.toString(),
                Identifiers.TYPE_INT);
    }

    private String getStanderMethodBodyHead() {
        return "SqlSession session = this.sqlSessionFactory.openSession(true);" + Identifiers.CRLF;
    }

    @Override
    public String text() {
        return builder.text();
    }

    @Override
    public void writeToFile(String filename) throws IOException {
        IJavaMapperBuilder.super.writeToFile(filename);
    }

    public static void main(String[] args) throws DocumentException {
        IJavaMapperBuilder mapperBuilder = new JavaMapperBuilder(new BuildingConfig());
        System.out.println(mapperBuilder.Build(new TableMetaData("test_table", new ArrayList<TableMetaData.Column>() {
            {
                add(new TableMetaData.Column("VARCHAR", "test_col"));
                add(new TableMetaData.Column("VARCHAR", "test_col"));
            }
        })));
    }
}
